package com.denlaku.longan.controller.impl;

import com.denlaku.longan.anno.Audit;
import com.denlaku.longan.anno.VarBy;
import com.denlaku.longan.audit.AuditVariables;
import com.denlaku.longan.controller.DashboardConfigController;
import com.denlaku.longan.http.Resp;
import com.denlaku.longan.http.Return;
import com.denlaku.longan.lock.Lock;
import com.denlaku.longan.lock.LockFactory;
import com.denlaku.longan.qo.DashboardQuery;
import com.denlaku.longan.qo.Query;
import com.denlaku.longan.qo.RefreshQuery;
import com.denlaku.longan.service.DashboardIndexService;
import com.denlaku.longan.service.DashboardService;
import com.denlaku.longan.service.ObjectTagService;
import com.denlaku.longan.service.RefreshService;
import com.denlaku.longan.service.TenantMemObjPermService;
import com.denlaku.longan.util.Objects;
import com.denlaku.longan.util.ThreadPoolUtil;
import com.denlaku.longan.vo.ComponentData;
import com.denlaku.longan.vo.Dashboard;
import com.denlaku.longan.vo.PageBy;
import com.github.pagehelper.PageHelper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.function.Consumer;
import java.util.stream.Collectors;

/**
 * @author tianx
 */
@RestController
public class DashboardConfigControllerImpl implements DashboardConfigController {
    @Autowired
    private DashboardService dashboardService;

    @Autowired
    private DashboardIndexService dashboardIndexService;

    @Autowired
    private RefreshService refreshService;

    @Autowired
    private ObjectTagService objectTagService;

    @Autowired
    private LockFactory lockFactory;

    @Autowired
    private TenantMemObjPermService tenantMemObjPermService;

    @Override
    @Audit(message = "add new dashboard: {#dashboard.title}", operation = "audit.operation.addDashboard", module = "Dashboard")
    public Return<Long> add(@RequestBody Dashboard dashboard) {
        dashboardService.add(dashboard);
        buildIndex(dashboard);
        return Resp.success(dashboard.getId());
    }

    @Override
    @Audit(message = "update dashboard: {#dashboard.title}", operation = "audit.operation.updateDashboard", module = "Dashboard")
    public Return<Long> update(@RequestBody Dashboard dashboard) {
        return updateDashboard(dashboard, dashboardService::update);
    }

    @Override
    @Audit(message = "update dashboard: {#dashboard.title}", operation = "audit.operation.updateDashboard", module = "Dashboard")
    public Return<Long> config(Dashboard dashboard) {
        return updateDashboard(dashboard, dashboardService::config);
    }

    private Return<Long> updateDashboard(Dashboard dashboard, Consumer<Dashboard> consumer) {
        if (dashboard.getId() == null) {
            return Resp.success();
        }
        Lock lock = lockFactory.getLock(String.valueOf(dashboard.getId()));
        if (!lock.tryLock()) {
            return Resp.error("请稍后重试");
        }
        try {
            consumer.accept(dashboard);
            buildIndex(dashboard);
        } finally {
            lock.unlock();
        }
        return Resp.success();
    }

    private void buildIndex(Dashboard dashboard) {
        ThreadPoolUtil.execAsync(() -> dashboardIndexService.buildIndex(dashboard));
    }

    @Override
    @Audit(message = "delete dashboard: {#title}", operation = "audit.operation.deleteDashboard", module = "Dashboard")
    public Return<Void> delete(Long id) {
        Dashboard dashboard = dashboardService.getBase(Query.of(id));
        String title = Optional.ofNullable(dashboard).map(Dashboard::getTitle).orElse(String.valueOf(id));
        AuditVariables.set("title", title);
        dashboardService.delete(Query.of(id));
        ThreadPoolUtil.execAsync(() -> dashboardIndexService.buildIndex(dashboard));
        return Resp.success();
    }

    @Override
    public Return<Dashboard> get(@RequestParam("id") Long id) {
        Dashboard dashboard = dashboardService.get(Query.of(id));
        return Resp.success(dashboard);
    }

    @VarBy
    @Override
    public Return<List<Dashboard>> pageList(@RequestBody DashboardQuery query, PageBy pageBy) {
        tenantMemObjPermService.setPermTagIds(query);
        PageHelper.startPage(pageBy.getPage(), pageBy.getSize());
        List<Dashboard> list = dashboardService.list(query);
        setTagName(query.getTenantId(), list);
        return Resp.success(list);
    }

    private void setTagName(Long tenantId, List<Dashboard> list) {
        Set<Long> tagIds = list.stream().map(Dashboard::getTagId).filter(Objects::nonNull).collect(Collectors.toSet());
        Map<Long, String> tagMap = objectTagService.getTagNameMap(Query.of(tenantId, tagIds));
        for (Dashboard dashboard : list) {
            dashboard.setTagName(tagMap.get(dashboard.getTagId()));
        }
    }

    @Override
    public Return<ComponentData> refresh(@RequestBody RefreshQuery query) {
        query.setDebug(true);
        ComponentData refresh = refreshService.refresh(query);
        return Resp.success(refresh);
    }

    @Override
    public Return<List<ComponentData>> refreshes(@RequestBody List<RefreshQuery> queries) {
        List<ComponentData> dataList = refreshService.refreshes(queries, true);
        return Resp.success(dataList);
    }

}
